/*
 * main.c
 *
 *  Created on: May 29, 2013
 *      Author: Aden
 */


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ncurses/curses.h>
#include "shared_memory.h"
#include "global.h"
#include "queue.h"
#include "pcb.h"
#include "process.h"
#include "cpu.h"
#include "kbd_thread.h"

void*  x(void* y)
{
  return y;
}


/*
 * Example: $./scheduler -t 22 -k 2 -i 3 -p 3
 *
 * -t   total number of processes
 * -k   number of keyboard processes
 * -i   number of io processes (not keyboard)
 * -p   number of producer-consumer pairs
 *
 * Processes not designated at command line (-k, -io, -pc) are compute-bound.
 */

 int main(int argc, char *argv[]) {
        initscr();
        pthread_t the_kbd_thread;
        int rc = pthread_create(&the_kbd_thread, NULL, kbd_thread, NULL);
        
        srand(time(NULL));
        // Initialize counts.  Paired processes will use up two counts.
        int total_process_count = 0;
        int keyboard_process_count = 0;
        int io_process_count = 0;
        int paired_process_count = 0;
        int computing_process_count = 0;


        // Do we want to assume that command line instructions will be rational?
        // Or do we service requests as best we can?
        // For instance: scheduler.exe -p 5 -k 1 -io 2 -pc 2 --> says start 5 processes, 1 keyboard, 2 io, 2 paired
        // which is actually 7 processes.  Do we bump the processes to 7, do we not run computing processes?

        int loop_counter;
        int char_counter;
        int arg_length;
        // starts at argv[1] because argv[0] is the execution command itself.
        for(loop_counter = 1; loop_counter < argc; loop_counter++) {
                switch ( (int) argv[loop_counter][0]) {
                case '-':
                        arg_length = strlen(argv[loop_counter]);
                        for(char_counter = 1; char_counter < arg_length; ++char_counter) {
                                switch( (int) argv[loop_counter][char_counter]) {
                                case 't' :
                                        total_process_count = atoi(argv[++loop_counter]);
                                        break;
                                case 'k' :
                                        keyboard_process_count = atoi(argv[++loop_counter]);
                                        break;
                                case 'i' :
                                        io_process_count = atoi(argv[++loop_counter]);
                                        break;
                                case 'p' :
                                        paired_process_count = atoi(argv[++loop_counter]);
                                        break;

                                }
                        }
                        break;
                }
        }

        computing_process_count = total_process_count;
        computing_process_count -= 2 * paired_process_count;
        computing_process_count -= io_process_count;
        computing_process_count -= keyboard_process_count;

        int process_pile[] = {keyboard_process_count, io_process_count, paired_process_count, computing_process_count};

        int number_of_processes = total_process_count + paired_process_count; // each paired process has two processes.
//printw("[err]process count: %d\n", number_of_processes);
        PCBPtr process_heap[number_of_processes];

printw("[err]process count complete\n");
        populate_heap(process_heap, process_pile, number_of_processes);
printw("[err]heap constructed\n");
        run_cpu(process_heap, total_process_count);
printw("\nI never thought I'd see this message during runtime.\nPress 'q' to exit.\n");
   /* Last thing that main() should do */
   endwin();
   pthread_exit(NULL);
}

void create_process(int process_type, PCBPtr* temp, int proc_id) {
        int time_to_start = rand() % MAX_CYCLE;
        (*temp) = newPcbConstructor();
        (*temp)->setTimeSlice((*temp), time_to_start);
		(*temp)->setPID((*temp), proc_id);

        // this translates from the constants used by main to the constants used by process
        int main_to_proc[] = {KEYBOARD, IO, NETWORK_SERVICE, COMPUTE, NETWORK_SERVICE, NETWORK_SERVICE};
        (*temp)->proc->setProcType((*temp)->proc, main_to_proc[process_type]);
        (*temp)->setState((*temp), NEW);
		(*temp)->setBurstTime((*temp), (rand() % 33) + 10);
}

void create_paired_process(PCBPtr* prod, PCBPtr* cons, int proc_id) {
        SharedPtr shared_data = (SharedPtr)malloc(sizeof(SharedMem));
        create_process(PRODUCER_PROCESS, prod, proc_id);
        create_process(CONSUMER_PROCESS, cons, proc_id + 1);
        (*cons)->setTimeSlice((*cons), (*prod)->getTimeSlice(*prod));
  
  mutex_init(&(shared_data->lock));
  condition_init(&(shared_data->full));
  condition_init(&(shared_data->empty));
  shared_data->pair_number = (*prod)->pid;
  
  (*prod)->shared = shared_data;
  (*prod)->prod_cons_type = 'p';
  (*cons)->shared = shared_data;
  (*cons)->prod_cons_type = 'c';
  
}

populate_heap(PCBPtr heap[], int pile[], int size) {
        int counter = 0;
printw("[err]heap: %x\n",(int)heap);
        while(pile[KEYBOARD_PROCESS] > 0) {
//printw("[err]counter: %d\n", counter);
                create_process(KEYBOARD_PROCESS, &heap[counter], counter);
                pile[KEYBOARD_PROCESS]--;
printw("[err]kybd created\n");
                counter++;
        }
        while(pile[IO_PROCESS] > 0) {
                create_process(IO_PROCESS, &heap[counter], counter);
//printw("[err]counter: %d\n", counter);
                pile[IO_PROCESS]--;
printw("[err]io created\n");
                counter++;
        }
        while(pile[COMPUTE_PROCESS] > 0) {
//printw("[err]counter: %d\n", counter);
                create_process(COMPUTE_PROCESS, &heap[counter], counter);
                pile[COMPUTE_PROCESS]--;
printw("[err]compute created\n");
                counter++;
        }
        while(pile[PAIRED_PROCESS] > 0) {
//printw("[err]counter: %d\n", counter);
                create_paired_process(&heap[counter], &heap[counter + 1], counter);
                pile[PAIRED_PROCESS]--;
printw("[err]paired processes created\n");
                counter+=2;
        }

}
